package memory

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/google/uuid"
)

// RedisStore implements MemoryStore using Redis
type RedisStore struct {
	client *redis.Client
	config MemoryConfig
}

// NewRedisStore creates a new Redis-based memory store
func NewRedisStore(config *MemoryConfig) (MemoryStore, error) {
	if config.Address == "" {
		config.Address = "localhost:6379"
	}

	opt, err := redis.ParseURL(config.Address)
	if err != nil {
		return nil, fmt.Errorf("failed to parse Redis URL: %v", err)
	}

	client := redis.NewClient(opt)

	// Test connection
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := client.Ping(ctx).Err(); err != nil {
		return nil, fmt.Errorf("failed to connect to Redis: %v", err)
	}

	return &RedisStore{
		client: client,
		config: *config,
	}, nil
}

// Store implements MemoryStore.Store
func (s *RedisStore) Store(ctx context.Context, memory *MemoryEntry) error {
	if memory.ID == "" {
		memory.ID = uuid.New().String()
	}

	memory.UpdatedAt = time.Now()
	if memory.CreatedAt.IsZero() {
		memory.CreatedAt = memory.UpdatedAt
	}

	// Store the main memory data
	data, err := json.Marshal(memory)
	if err != nil {
		return fmt.Errorf("failed to marshal memory: %v", err)
	}

	key := fmt.Sprintf("memory:%s", memory.ID)
	if err := s.client.Set(ctx, key, data, 0).Err(); err != nil {
		return fmt.Errorf("failed to store memory: %v", err)
	}

	// Store embedding in a sorted set for similarity search
	if len(memory.Embedding) > 0 {
		embKey := fmt.Sprintf("embedding:%s", memory.Type)
		embData, err := json.Marshal(memory.Embedding)
		if err != nil {
			return fmt.Errorf("failed to marshal embedding: %v", err)
		}

		if err := s.client.HSet(ctx, embKey, memory.ID, embData).Err(); err != nil {
			return fmt.Errorf("failed to store embedding: %v", err)
		}
	}

	// Store type index
	typeKey := fmt.Sprintf("type:%s", memory.Type)
	if err := s.client.SAdd(ctx, typeKey, memory.ID).Err(); err != nil {
		return fmt.Errorf("failed to store type index: %v", err)
	}

	return nil
}

// Get implements MemoryStore.Get
func (s *RedisStore) Get(ctx context.Context, id string) (*MemoryEntry, error) {
	key := fmt.Sprintf("memory:%s", id)
	data, err := s.client.Get(ctx, key).Bytes()
	if err != nil {
		if err == redis.Nil {
			return nil, fmt.Errorf("memory not found: %s", id)
		}
		return nil, fmt.Errorf("failed to get memory: %v", err)
	}

	var memory MemoryEntry
	if err := json.Unmarshal(data, &memory); err != nil {
		return nil, fmt.Errorf("failed to unmarshal memory: %v", err)
	}

	return &memory, nil
}

// Search implements MemoryStore.Search
func (s *RedisStore) Search(ctx context.Context, query string, limit int) ([]*MemoryEntry, error) {
	pattern := fmt.Sprintf("memory:*")
	iter := s.client.Scan(ctx, 0, pattern, 0).Iterator()

	var memories []*MemoryEntry
	for iter.Next(ctx) {
		if len(memories) >= limit {
			break
		}

		data, err := s.client.Get(ctx, iter.Val()).Bytes()
		if err != nil {
			continue
		}

		var memory MemoryEntry
		if err := json.Unmarshal(data, &memory); err != nil {
			continue
		}

		// Simple content matching
		if query == "" || strings.Contains(strings.ToLower(memory.Content), strings.ToLower(query)) {
			memories = append(memories, &memory)
		}
	}

	return memories, nil
}

// SearchByEmbedding implements MemoryStore.SearchByEmbedding
func (s *RedisStore) SearchByEmbedding(ctx context.Context, embedding []float32, limit int) ([]*MemoryEntry, error) {
	return nil, fmt.Errorf("vector similarity search not implemented")
}

// List implements MemoryStore.List
func (s *RedisStore) List(ctx context.Context, filters map[string]interface{}, limit int) ([]*MemoryEntry, error) {
	pattern := fmt.Sprintf("memory:*")
	iter := s.client.Scan(ctx, 0, pattern, 0).Iterator()

	var memories []*MemoryEntry
	for iter.Next(ctx) {
		if len(memories) >= limit {
			break
		}

		data, err := s.client.Get(ctx, iter.Val()).Bytes()
		if err != nil {
			continue
		}

		var memory MemoryEntry
		if err := json.Unmarshal(data, &memory); err != nil {
			continue
		}

		// Apply filters
		match := true
		for k, v := range filters {
			if mv, ok := memory.Metadata[k]; !ok || mv != v {
				match = false
				break
			}
		}

		if match {
			memories = append(memories, &memory)
		}
	}

	return memories, nil
}

// ListByType implements MemoryStore.ListByType
func (s *RedisStore) ListByType(ctx context.Context, memoryType MemoryType, limit int) ([]*MemoryEntry, error) {
	typeKey := fmt.Sprintf("type:%s", memoryType)

	// Get IDs of memories of the specified type
	ids, err := s.client.SMembers(ctx, typeKey).Result()
	if err != nil {
		return nil, fmt.Errorf("failed to get type members: %v", err)
	}

	var memories []*MemoryEntry
	for _, id := range ids {
		if len(memories) >= limit {
			break
		}

		memory, err := s.Get(ctx, id)
		if err != nil {
			continue
		}

		memories = append(memories, memory)
	}

	return memories, nil
}

// Delete implements MemoryStore.Delete
func (s *RedisStore) Delete(ctx context.Context, id string) error {
	// Get memory first to remove from type index
	memory, err := s.Get(ctx, id)
	if err != nil {
		return err
	}

	// Remove from type index
	typeKey := fmt.Sprintf("type:%s", memory.Type)
	if err := s.client.SRem(ctx, typeKey, id).Err(); err != nil {
		return fmt.Errorf("failed to remove from type index: %v", err)
	}

	// Remove embedding if exists
	if len(memory.Embedding) > 0 {
		embKey := fmt.Sprintf("embedding:%s", memory.Type)
		if err := s.client.HDel(ctx, embKey, id).Err(); err != nil {
			return fmt.Errorf("failed to remove embedding: %v", err)
		}
	}

	// Remove main memory data
	key := fmt.Sprintf("memory:%s", id)
	if err := s.client.Del(ctx, key).Err(); err != nil {
		return fmt.Errorf("failed to delete memory: %v", err)
	}

	return nil
}

// Clear implements MemoryStore.Clear
func (s *RedisStore) Clear(ctx context.Context) error {
	// Get all memory keys
	pattern := fmt.Sprintf("memory:*")
	iter := s.client.Scan(ctx, 0, pattern, 0).Iterator()

	for iter.Next(ctx) {
		if err := s.client.Del(ctx, iter.Val()).Err(); err != nil {
			return fmt.Errorf("failed to clear memories: %v", err)
		}
	}

	// Clear type indices
	pattern = fmt.Sprintf("type:*")
	iter = s.client.Scan(ctx, 0, pattern, 0).Iterator()
	for iter.Next(ctx) {
		if err := s.client.Del(ctx, iter.Val()).Err(); err != nil {
			return fmt.Errorf("failed to clear type indices: %v", err)
		}
	}

	// Clear embeddings
	pattern = fmt.Sprintf("embedding:*")
	iter = s.client.Scan(ctx, 0, pattern, 0).Iterator()
	for iter.Next(ctx) {
		if err := s.client.Del(ctx, iter.Val()).Err(); err != nil {
			return fmt.Errorf("failed to clear embeddings: %v", err)
		}
	}

	return nil
}

// Close closes the Redis connection
func (s *RedisStore) Close() error {
	return s.client.Close()
}
